#!/usr/bin/env python


""" 
 Copyright 2006 Rob Cakebread <gentoodev@gmail.com>
 (Palmer@genesis mud)

 Released under the terms of the GPL-2 license

"""

import sys
import cPickle
import os
import time

from elementtree.ElementTree import Element, SubElement, dump
import pygtk
if sys.platform == 'win32':
    import jpath
else:
    pygtk.require('2.0')
import gtk 
import gtk.glade 
import gobject
from Cheetah.Template import Template

from dialogs import *
import utils


GLADE_FILE = 'roomaker.glade'
__version__ = "0.5"

#pylint: disable-msg=R0201,C0103

#Find directory .exe is running from for Win32 platforms:
if hasattr(sys,"frozen") and sys.frozen == "windows_exe":
    EXE_PATH = jpath.path(os.path.abspath(sys.executable)).dirname()


class MapperWindow:

    """Main application window"""

    def __init__(self, my_map):

        self.windowname = "window1" 
        self.w_tree = gtk.glade.XML(GLADE_FILE, self.windowname) 
        self.window = self.w_tree.get_widget("window1")
        self.window.connect("delete-event", self.cb_delete_event) 

        if not sys.platform == 'win32':
            self.window.set_icon_from_file("roomaker.png")
        # direction buttons
        self.window.set_title("Roomaker - (untitled)")
        button1 = self.w_tree.get_widget("button1")
        button1.connect("button-press-event", self.on_dir_click) 
        button2 = self.w_tree.get_widget("button2")
        button2.connect("button-press-event", self.on_dir_click) 
        button3 = self.w_tree.get_widget("button3")
        button3.connect("button-press-event", self.on_dir_click) 
        button4 = self.w_tree.get_widget("button4")
        button4.connect("button-press-event", self.on_dir_click) 
        button5 = self.w_tree.get_widget("button5")
        button5.connect("button-press-event", self.on_dir_click) 
        button6 = self.w_tree.get_widget("button6")
        button6.connect("button-press-event", self.on_dir_click) 
        button7 = self.w_tree.get_widget("button7")
        button7.connect("button-press-event", self.on_dir_click) 
        button8 = self.w_tree.get_widget("button8")
        button8.connect("button-press-event", self.on_dir_click) 
        button9 = self.w_tree.get_widget("button9")
        button9.connect("button-press-event", self.on_dir_click) 
        button10 = self.w_tree.get_widget("button10")
        button10.connect("button-press-event", self.on_dir_click) 
        button11 = self.w_tree.get_widget("button11")
        button11.connect("button-press-event", self.on_dir_click) 
        #Delete room 
        button12 = self.w_tree.get_widget("button14")
        button12.connect("button-press-event", self.on_delete_clicked) 
        #all directions
        button15 = self.w_tree.get_widget("button15")
        button15.connect("button-press-event", self.on_dir_click) 
        #all directions and down
        button16 = self.w_tree.get_widget("button16")
        button16.connect("button-press-event", self.on_dir_click) 

        radiobutton1 = self.w_tree.get_widget("radiobutton1")
        radiobutton1.connect("toggled", self.on_numbering) 

        #new
        new_button = self.w_tree.get_widget("toolbutton1")
        new_button.connect("clicked", self.new_file)
        #load
        open_button = self.w_tree.get_widget("toolbutton2")
        open_button.connect("clicked", self.select_map_file) 

        #save
        self.save_button = self.w_tree.get_widget("toolbutton3")
        self.save_button.connect("clicked", self.write_room_files) 
        self.save_button.set_sensitive(False)
        self.saved = True

        #new
        self.menu = self.w_tree.get_widget("imagemenuitem1")
        self.menu.connect("activate", self.new_file)
        #load
        self.menu = self.w_tree.get_widget("imagemenuitem2")
        self.menu.connect("activate", self.select_map_file) 

        #save
        self.menu = self.w_tree.get_widget("imagemenuitem3")
        self.menu.connect("activate", self.write_room_files) 

        #save as
        self.menu = self.w_tree.get_widget("menuitem2")
        self.menu.connect("activate", self.save_as) 

        #quit
        self.menu = self.w_tree.get_widget("imagemenuitem5")
        self.menu.connect("activate", self.on_destroy_event) 

        #about
        self.menu = self.w_tree.get_widget("imagemenuitem10")
        self.menu.connect("activate", on_about) 

        self.radiobutton1 = self.w_tree.get_widget("radiobutton1")
        self.radiobutton2 = self.w_tree.get_widget("radiobutton2")
        self.radiobutton2.set_group(self.radiobutton1)

        self.scrolled_window = self.w_tree.get_widget("scrolledwindow1")
        self.area = self.w_tree.get_widget("drawingarea1")

        self.area.connect("expose-event", self.draw)
        self.area.add_events(gtk.gdk.KEY_PRESS | 
                gtk.gdk.POINTER_MOTION_MASK | 
                gtk.gdk.BUTTON_PRESS_MASK | 
                gtk.gdk.BUTTON_RELEASE_MASK)

        self.area.connect("key_press_event", self.get_keypress) 
        #Drag a room:

        self.dragx = 0
        self.dragy = 0
        self.dragroom = None
        self.area.connect("button-press-event", self.dragRoomStart)
        self.area.connect("button-release-event", self.dragRoomStop)
        self.area.connect("motion-notify-event", self.dragRoomMove)
        #End of drag
        self.square_size = 30
        self.padding = 5
        self.my_map = my_map
        self.scrolled_window.set_size_request(
                self.padding * 2 * self.square_size, 
                self.padding * 2 * self.square_size)

        self.hbox = self.w_tree.get_widget("hbox4")
        self.vbox = self.w_tree.get_widget("vbox1")

        self.roominfo = RoomInfo(self.vbox, self.my_map, self)
        self.roominfo.update(self.my_map.current_room)

        self.combobox1 = self.w_tree.get_widget("combobox1")
        store = gtk.ListStore(gobject.TYPE_STRING)
        self.combobox1.set_model(store)
        cell = gtk.CellRendererText()
        self.combobox1.pack_start(cell, True)
        self.combobox1.add_attribute(cell, 'text', 0)
        self.filename = None
        self.template_filenames = None
        self.handle_move()
        self.set_template_combo()
        self.area.set_flags(gtk.CAN_FOCUS)
        self.area.grab_focus()

    def on_numbering(self, event = None):
        """Determine room filename numbering"""
        self.roominfo.update_numbering_scheme()

    def set_template_combo(self):
        """Load combobox with all template filenames"""
        self.template_filenames = utils.query_templates()
        for filename in self.template_filenames:
            self.combobox1.append_text(filename)
        self.combobox1.set_active(0)

    def query_template_filename(self):
        """Get template filename from combobox"""
        return self.combobox1.get_active_text()

    def select_map_file(self, event):
        """Select a .map file to load"""
        if not self.verify_save():
            return
        chooser = gtk.FileChooserDialog(title="Choose a map",
                action=gtk.FILE_CHOOSER_ACTION_OPEN,
                buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,
                    gtk.STOCK_OPEN,gtk.RESPONSE_OK))
        my_filter = gtk.FileFilter()
        my_filter.set_name("Maps")
        my_filter.add_pattern("*.map")
        chooser.add_filter(my_filter)
        chooser.run()
        filename = chooser.get_filename()
        chooser.destroy()
        if filename[-4:] != ".map":
            return
        self.my_map.load_map(filename)
        self.filename = filename
        self.handle_move()

    def new_file(self, event):
        """Clear map"""
        if self.verify_save():
            self.window.set_title("Roomaker - (untitled)")
            self.my_map.new_map()
            self.handle_move()
            self.filename = None


    def save_as(self, event):
        """Save as file dialog"""
        chooser = gtk.FileChooserDialog(title=None,
                      action=gtk.FILE_CHOOSER_ACTION_SAVE,
                      buttons=(gtk.STOCK_CANCEL,
                        gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE,gtk.RESPONSE_OK))
        chooser.run()
        filename = chooser.get_filename()
        chooser.destroy()
        try:
            if filename[-4:] != ".map":
                filename += ".map"
        #pressed cancel
        except TypeError:
            return
        self.filename = filename
        self.write_room_files(None)

    def parse_cheetah(self, template, room, date, filename ):
        """Parse and return text from a cheetah template"""
        return Template(file = template, 
                        compilerSettings={"directiveStartToken" : "@"},
                        searchList=[{'room': room,
                                    'date': date,
                                    'filename': filename
                                    }]
                        )

    def set_prefix_suffix(self, sequential):
        """Set prefix and suffix for each room"""
        for room in self.my_map.rooms:
            if sequential:
                room.suffix = "%02d" % int(room.id)
            else:
                #X-Y Coordinates
                room.suffix = "%02d%02d" % (int(room.y), int(room.x))
            if room.template[-2:] == '.c':
                room.prefix = room.template[:-2]
            else:
                #Cheetah template
                room.prefix = room.template[:-5]
            room.filename = room.prefix + room.suffix

    def write_room_files(self, event):
        """Write an LPC file for each room using template"""
        #Use sequential numbering for room file suffixes
        sequential = True
        if not MAPPER.mapper_window.radiobutton1.get_active():
            #Use coordinates for suffixes
            sequential = False

        if not self.filename:
            chooser = gtk.FileChooserDialog(title=None,
                          action=gtk.FILE_CHOOSER_ACTION_SAVE,
                          buttons=(gtk.STOCK_CANCEL,
                          gtk.RESPONSE_CANCEL,
                          gtk.STOCK_SAVE,gtk.RESPONSE_OK))
            chooser.run()
            filename = chooser.get_filename()
            chooser.destroy()
            try:
                if filename[-4:] != ".map":
                    filename += ".map"
            #pressed cancel
            except TypeError:
                return
            self.filename = filename

        self.my_map.save_map(self.filename)
        utils.delete_room_files()
        self.set_prefix_suffix(sequential)

        #Generate each LPC file from templates
        date = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()))
        for room in self.my_map.rooms:
            out_filename = room.prefix + room.suffix + ".c"
            template_path = "%s/%s" % (utils.query_template_dir(), 
                                       room.template)
            out = self.parse_cheetah(template_path, room, date, out_filename )
            
            if room.prefix == "water":
                fname = "output/%s_%s_%s.c" % (room.x, room.y, room.z)
            else:
                fname = "output/%s%s.c" % (room.prefix, room.suffix)
            open(fname, "w").write(str(out))
        self.set_saved()
        self.window.set_title("Roomaker - %s" % os.path.basename(self.filename))
        return True

    def set_saved(self):
        """Set saved state of file"""
        self.save_button.set_sensitive(False)
        self.saved = True

    def set_unsaved(self):
        """Set saved state of file"""
        self.save_button.set_sensitive(True)
        self.saved = False

    def cb_delete_event(self, widget, event):
        """Exit app if window is destroyed"""
        if self.verify_save():
            gtk.main_quit()
            return True
        return True

    def on_destroy_event(self, widget):
        """Exit app"""
        if self.verify_save():
            gtk.main_quit()

    def verify_save(self):
        """Verify new map dialog"""
        if not self.saved:
            dialog = gtk.Dialog(title="Save changes to map?",
                    buttons=(
                        gtk.STOCK_YES,gtk.RESPONSE_YES,
                        gtk.STOCK_NO, gtk.RESPONSE_NO,
                        gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL)
                    )
        else:
            return True
        response = dialog.run()
        dialog.destroy()
        if response == gtk.RESPONSE_CANCEL:
            return False
        elif response == gtk.RESPONSE_YES:
            result = self.write_room_files(None)
            if result:
                return True
        elif response == gtk.RESPONSE_NO:
            return True
        

    def on_dir_click(self, widget, event):
        """Catch button movement presses and move"""
        button_map = {'button1': 'northwest', 'button2': 'north', 
               'button3': 'northeast', 'button4': 'west', 
               'button5': 'undo', 'button6': 'east', 'button7': 'southwest', 
               'button8': 'south', 'button9': 'southeast',
               'button10': 'up', 'button11': 'down', 'button15': 'all',
               'button16': 'alldown'
               }
        button = button_map[widget.name]
        MAPPER.handle_input(button)

    def on_delete_clicked(self, widget, event):
        """Delete room from map"""
        MAPPER.del_room(self.my_map.current_room.id)

    def get_keypress(self, widget, event):
        """Process key presses"""
        #TODO Add up and down
        #Numeric keypad values:
        key_map = {65464: "n", 65460: "w", 65458: "s", 65462: "e",
                   65465: "ne", 65463: "nw", 65459: "se", 65457: "sw",
                   65461: "undo"
                   }

        try:
            key_press = key_map[event.keyval]
        except KeyError:
            return

        MAPPER.handle_input(key_press)


    def draw(self, area, event):
        """Draw a room"""
        canvas = self.area.window
        colormap = canvas.get_colormap()
        width, height = self.area.get_size_request()
        gc = canvas.new_gc()
        gc.set_foreground(colormap.alloc_color( "light grey" ) )
        canvas.draw_rectangle(gc, True, 0, 0, width, height)
        self.draw_rooms()

    def draw_rooms(self):
        """Draw series of rooms"""
        canvas = self.area.window
        colormap = canvas.get_colormap()
        
        gc = canvas.new_gc()
        colors = ("Black", "White", "Purple", "Green", "Red", "Yellow", "Blue", "Brown")
        
        for room in self.my_map.rooms:
            if room.z  != self.my_map.current_room.z:
                continue
            x, y = self.convert_to_area_coords(room.x, room.y)
            if not room.template:
                room.template = self.query_template_filename()
            try:
                template_color = self.template_filenames.index(room.template)
            except IndexError:
                #XXX ran out of colors
                template_color = "Black"
            #Draw room
            gc.set_foreground(colormap.alloc_color(colors[template_color]))
            canvas.draw_rectangle(gc, False,
                    x + self.square_size / 5, y + self.square_size / 5,
                    int(0.6 * self.square_size), int(0.6 * self.square_size))
            #Draw pointer for current room
            if room == self.my_map.current_room:
                canvas.draw_arc(gc, True, 
                        int(x + self.square_size / 2 - self.square_size * 0.3),
                        int(y + self.square_size / 2 - self.square_size * 0.3),
                        int(self.square_size * 0.6), 
                        int(self.square_size * 0.6),
                        0, 23040)
            #Draw exit lines
            for this_exit, r in room.exits.items():
                if r.z  < room.z:
                    #Down exit
                    canvas.draw_rectangle(gc, True,
                            x + self.square_size / 5, 12 + y + self.square_size / 5,
                            int(0.2 * self.square_size), int(0.2 * self.square_size))
                    continue
                elif r.z  > room.z:
                    #Up exit
                    canvas.draw_rectangle(gc, True,
                            12 + x + self.square_size / 5, y + self.square_size / 5,
                            int(0.2 * self.square_size), int(0.2 * self.square_size))
                    continue
                x0, y0 = self.convert_to_area_coords(r.x, r.y)
                canvas.draw_line(gc, x + self.square_size / 2,
                        y + self.square_size / 2, x0 + self.square_size / 2,
                        y0 + self.square_size / 2)

    def handle_move(self):
        """Update screen when we move to another room"""
        w = self.my_map.rightbound*self.square_size - self.my_map.leftbound * \
                self.square_size + self.square_size
        h = self.my_map.bottombound*self.square_size - self.my_map.topbound * \
                self.square_size + self.square_size

        if self.dragroom:
            room = self.dragroom
        else:
            room = self.my_map.current_room

        x, y = self.convert_to_area_coords(room.x - self.padding, 
                room.y - self.padding)
        vadj = self.scrolled_window.get_vadjustment()
        vadj.set_value(y)
        hadj = self.scrolled_window.get_hadjustment()
        hadj.set_value(x)

        self.area.set_size_request(w + self.padding * self.square_size * 2,
                h + self.padding * self.square_size * 2)
            
        self.area.queue_draw()
        self.roominfo.update(self.my_map.current_room)

    def convertToRoomCoords(self, x, y):
        """Convert x y coords to room location"""
        return (int(x / self.square_size) - \
                self.my_map.left_offset - self.padding), \
                (int(y / self.square_size) - \
                self.my_map.top_offset - self.padding)

    def convert_to_area_coords(self, x, y):
        """Convert x y coords to area location"""
        return ((x + self.my_map.left_offset + self.padding) * \
                self.square_size), ((y + self.my_map.top_offset + \
                self.padding) * self.square_size)

    def dragRoomStart(self, widget, event):
        """Grab a room with pointer"""
        x, y = self.convertToRoomCoords(event.x, event.y)
        room = self.my_map.room_by_coords(x, y, self.my_map.current_room.z)

        if event.button == 1:
            if room:
                self.dragroom = room

        #Right-click
        elif event.button == 3:
            if room:
                #Move 'dot' to an existing room
                self.my_map.move_to_room(room)
                self.handle_move()
            else:
                #Create new room where we click and move pointer to new room
                self.my_map._add_new_room(x, y)
                self.my_map.move_to_room(self.my_map.room_by_coords(x,
                                         y, 
                                         self.my_map.current_room.z)
                                         )
                self.area.queue_draw()

    def dragRoomStop(self, widget, event):
        """Drop room after dragging it"""
        if event.button == 1 and self.dragroom:
            x, y = self.convertToRoomCoords(event.x, event.y)
            self.my_map.moveRoomTo(self.dragroom, x, y)
            self.handle_move()
            self.dragroom = None

    def dragRoomMove(self, widget, event):
        """Move a room with pointer"""
        if self.dragroom:
            x, y = self.convertToRoomCoords(event.x, event.y)
            if self.dragroom.x  != x or self.dragroom.y  != y:
                if not \
                 self.my_map.room_by_coords(x, y, self.my_map.current_room.z):
                    self.dragroom.x = x
                    self.dragroom.y = y
                    self.area.queue_draw()

class Room:

    """Contains base room properties"""

    def __init__(self, id, template=None, x=0, y=0, z=0, exits=None):
        self.x = x
        self.y = y
        self.z = z
        self.id = id
        self.template = template
        
        if exits is None:
            self.exits = {}
        else:
            self.exits = exits

class UndoEntry:

    """Move backwards"""

    def __init__(self, map, previous_room):
        self.my_map = map
        self.previous_room = previous_room

    def execute(self):
        pass

class UndoMove(UndoEntry):

    """Go in reverse direction"""

    def execute(self):
        self.my_map.move_to_room(self.previous_room)

class UndoDig(UndoEntry):

    """Go in reverse direction and remove room from map"""

    def execute(self):
        self.my_map.removeRoom(self.my_map.current_room)
        self.my_map.move_to_room(self.previous_room)

class UndoExit(UndoEntry):

    """Undo exit"""

    def __init__(self, map, previous_room, dir1, dir2):
        UndoEntry.__init__(self, map, previous_room)
        self.dir1 = dir1
        self.dir2 = dir2

    def execute(self):
        """Perform the undo"""
        del self.previous_room.exits[self.dir1]
        del self.my_map.current_room.exits[self.dir2]

        self.my_map.move_to_room(self.previous_room)


class Map:

    """Map class"""

    def __init__(self):
        self.new_map()

        self._movement = {'north': (0, -1, 0),
                        'northeast': (1, -1, 0),
                        'east': (1, 0, 0),
                        'southeast': (1, 1, 0),
                        'south': (0, 1, 0),
                        'southwest': (-1, 1, 0),
                        'west': (-1, 0, 0),
                        'northwest': (-1, -1, 0),
                        'up': (0, 0, 1),
                        'down': (0, 0, -1) }
        self._opposite = {'north': 'south',
                        'northeast': 'southwest',
                        'east': 'west',
                        'southeast': 'northwest',
                        'south': 'north',
                        'southwest': 'northeast',
                        'west': 'east',
                        'northwest': 'southeast',
                        'up': 'down',
                        'down': 'up' }

        self.undo_stack = []

    def move_to_room(self, room):
        """Move to room"""
        if room not in self.rooms:
            return
        self.current_room = room

    def save_map(self, file):
        """Write map to file"""
        #With Linux I get recursion too deep errors with maps over 1000 rooms
        #So we raise the recursion limit temporarily during map saves
        nbr_rooms = len(self.rooms)
        #Get current system recursion limit
        recurs_limit = sys.getrecursionlimit()
        if recurs_limit < nbr_rooms:
            #Set larger recursion limit
            sys.setrecursionlimit(nbr_rooms + nbr_rooms / 2)
        cPickle.dump((self.current_id, self.leftbound, self.rightbound,
            self.topbound, self.bottombound, self.left_offset, 
            self.top_offset, self.rooms), open(file, "w"))
        #Restore default recursion limit
        sys.setrecursionlimit(recurs_limit)


    def dump_xml_map(self, filename):
        """Write XML map file"""
        my_map = Element("Map")
        for room in self.rooms:
            my_room = SubElement(my_map, "room", id=room.id)
            template = SubElement(my_room, "template", file = room.template)
            exits = SubElement(my_room, "Exits")
            for direction in room.exits.keys():
                my_exit = SubElement(exits, direction, exit=room.exits[direction].id)
        dump(my_map)

    def load_map(self, filename):
        """Load map from disk"""
        self.current_id, self.leftbound, self.rightbound, self.topbound, \
            self.bottombound, self.left_offset, self.top_offset, self.rooms = \
            cPickle.load(open(filename, "r"))
        self.current_room = self.room_by_id("1")
    
    def move(self, direction, template):
        """Move one room"""
        if direction in self.current_room.exits.keys():
            self.undo_stack.append(UndoMove(self, self.current_room))
            self._move_to_room(direction)
        else:
            #Create new room
            if direction in self._movement.keys():
                return self._dig_new_room(direction, template)

    def new_map(self):
        """Initialize new map"""
        self.rooms = [Room("1")]
        self.current_room = self.rooms[0]
        self.current_id = 2
        self.leftbound = 0
        self.rightbound = 100
        self.topbound = 0
        self.bottombound = 100
        self.left_offset = 0
        self.top_offset = 0

    def moveRoomTo(self, room, x, y):
        """Move to room by x,y coordinates"""
        room.x = x
        room.y = y
        self._new_bounds(x, y)

    def room_by_coords(self, x, y, z):
        """Find room by its x,y,z coordinates"""
        for room in self.rooms:
            if room.x == x and room.y == y and room.z == z:
                return room

    def room_by_id(self, id):
        """Returns room object by its id"""
        for room in self.rooms:
            if room.id == id:
                return room

    def _getopposite(self, direction):
        """Given a direction, return opposite direction"""
        if direction in self._opposite.keys():
            return self._opposite[direction]
        else:
            return direction

    def _move_to_room(self, direction):
        """Move to room"""
        self.current_room = self.current_room.exits[direction]

    def _new_bounds(self, newx, newy):
        """Set new bounds"""
        if newx < self.leftbound:
            self.leftbound = newx
            self.left_offset = abs(self.leftbound)
        if newx > self.rightbound:
            self.rightbound = newx
        if newy < self.topbound:
            self.topbound = newy
            self.top_offset = abs(self.topbound)
        if newy > self.bottombound:
            self.bottombound = newy

    def _add_new_room(self, x, y):
        """Add new room by right-clicking"""
        template = MAPPER.mapper_window.query_template_filename()
        room = Room(str(self.current_id), 
                template,
                x, y,
                z = self.current_room.z, 
                exits = {}
                )
        self.current_id += 1
        self._new_bounds(x, y)
        #self.current_room.exits[direction] = room
        self.rooms.append(room)
        self.undo_stack.append(UndoDig(self, self.current_room))
        #self._move_to_room(direction)
        MAPPER.mapper_window.set_unsaved()

    def _dig_new_room(self, direction, template):
        """Create new exit in room"""
        xoff, yoff, zoff = self._movement[direction]
        newx = self.current_room.x + xoff
        newy = self.current_room.y + yoff
        newz = self.current_room.z + zoff

        if MAPPER.mapper_window.radiobutton1.get_active():
            #Sequential
            numbering = 0
        else:
            #Coordinates
            numbering = 1

        #For starting room, use second room's template:
        if not self.current_room.template:
            self.current_room.template = template

        room = self.room_by_coords(newx, newy, newz)
        if not room:
            room = Room(str(self.current_id), 
                    template,
                    x = newx, y = newy,
                    z = newz, 
                    exits = {self._getopposite(direction): self.current_room}
                    )
            self.current_id += 1
            self._new_bounds(newx, newy)
            self.current_room.exits[direction] = room
            self.rooms.append(room)
            self.undo_stack.append(UndoDig(self, self.current_room))
            self._move_to_room(direction)
        else:
            #Room exists just add new exit
            self.current_room.exits[direction] = room
            room.exits[self._getopposite(direction)] = self.current_room
            self.undo_stack.append(UndoExit(self, self.current_room, 
                direction, self._getopposite(direction)))
            self._move_to_room(direction)
        MAPPER.mapper_window.set_unsaved()

    def undo(self):
        """Undo room"""
        try:
            entry = self.undo_stack.pop()
            entry.execute()
            MAPPER.mapper_window.set_unsaved()
        except IndexError:
            pass

    def removeRoom(self, target):
        """Remove room"""
        for room in self.rooms:
            for k, v in room.exits.items():
                if v == target:
                    del room.exits[k]

        self.rooms.remove(target)
        MAPPER.mapper_window.set_unsaved()

class Mapper:

    """Mapper class"""

    def __init__(self):
        self.my_map = Map()
        self.mapper_window = MapperWindow(self.my_map)
        self.recognize = ['north', 'northeast', 'east', 'southeast', 'south',
                'southwest', 'west', 'northwest', 'up', 'down']
        self.mapping = False
        

    def start_mapping(self):
        """Allow mapping"""
        self.mapping = True

    def stop_mapping(self):
        self.mapping = False


    def handle_input(self, data):
        """Handle keyboard input"""
        if data == "undo":
            self.my_map.undo()
            self.mapper_window.handle_move()
            return
        template = self.mapper_window.query_template_filename()
        if data in self.my_map.current_room.exits.keys():
            self.my_map.move(data, template)
            self.mapper_window.handle_move()
        elif data in self.recognize:
            self.my_map.move(data, template)
            self.mapper_window.handle_move()
        elif data == "all":
            dirs = ['east', 'west', 'west', 'east', 'south', 'north',
                    'north', 'south', 'southeast', 'northwest', 'southwest',
                    'northeast', 'northeast', 'southwest', 'northwest',
                    'south', 'south', 'east', 'east', 'north', 'north',
                    'west', 'west', 'south', 'northeast', 'southeast',
                    'southwest', 'northwest', 'southeast']
            for direction in dirs:
                self.my_map.move(direction, template)
            self.mapper_window.handle_move()

        elif data == "alldown":
            dirs = ['down','up', 'east', 'down', 'up', 'west', 'west', 'down',
                    'up', 'east', 'south', 'down', 'up', 'north', 'north',
                    'down', 'up', 'south', 'southeast', 'down', 'up', 
                    'northwest', 'down', 'up', 'southwest', 'down', 'up', 
                    'northeast', 'northeast', 'down', 'up', 'southwest', 
                    'northwest', 'down', 'up', 'south', 'south', 'east', 
                    'east', 'north', 'north', 'west', 'west', 'south', 
                    'northeast', 'southeast', 'southwest', 'northwest', 
                    'southeast']
            for direction in dirs:
                self.my_map.move(direction, template)
            self.mapper_window.handle_move()

        
    def show(self):
        """Show mapper window"""
        self.mapper_window.window.show()

    def del_room(self, id):
        """Delete room"""
        room = self.my_map.room_by_id(id)
        if room:
            self.my_map.removeRoom(room)
            #FIXME: We move to the first room when we delete a room.
            #Try to move to an adjacent room first, then one close by.
            print "Delete..."
            self.my_map.move_to_room(self.my_map.room_by_id("1"))
            self.mapper_window.handle_move()



class RoomInfo:
    
    """Contains room info notebook"""

    def __init__(self, parent, map, mapperwindow):
        self.w_tree = gtk.glade.XML(GLADE_FILE, "window1") 
        self.my_map = map
        self.mapperwindow = mapperwindow
        self.template_entry = self.w_tree.get_widget("template_entry")
        self.suffix_entry = self.w_tree.get_widget("suffix_entry")
        self.coords_entry = self.w_tree.get_widget("coords_entry")

        self.template_entry.set_editable(False)
        button = self.w_tree.get_widget("button17")
        button.connect("clicked", self.change_template)
        self.suffix_entry.set_editable(False)
        self.coords_entry.set_editable(False)

    def set_template_combo(self):
        """Load combobox with all template filenames"""
        self.template_filenames = utils.query_templates()
        for filename in self.template_filenames:
            self.combobox2.append_text(filename)
        self.combobox2.set_active(0)

    def change_template(self, event):
        """Show about dialog"""
        def close(widget):
            dialog.destroy()
        self.combobox2 = self.w_tree.get_widget("combobox2")
        store = gtk.ListStore(gobject.TYPE_STRING)
        self.combobox2.set_model(store)
        cell = gtk.CellRendererText()
        self.combobox2.pack_start(cell, True)
        self.combobox2.add_attribute(cell, 'text', 0)
        self.set_template_combo()

        dialog = self.w_tree.get_widget("dialog1")
        dialog.show()
        dialog.set_default_response(gtk.RESPONSE_OK)
        cancel = self.w_tree.get_widget("button12")
        ok = self.w_tree.get_widget("button13")
         
        result = dialog.run()
        self.update_template_entry()
        dialog.destroy()

    def update_template_entry(self):
        """Update template for current room"""
        self.room.template = self.combobox2.get_active_text()
        self.template_entry.set_text(self.room.template[:-5])

    def update(self, room):
        """update"""
        self.room = room

        if room.template:
            self.template_entry.set_text(self.room.template[:-5])
        self.update_numbering_scheme()

    def update_numbering_scheme(self):
        """Update to current numbering scheme"""
        if self.mapperwindow.radiobutton1.get_active():
            self.suffix_entry.set_text("%s" % (self.room.id))
        else:
            self.suffix_entry.set_text("%02d%02d" % (self.room.y, self.room.x))
        self.coords_entry.set_text("%s,%s,%s" %  \
                              (str(self.room.x), str(self.room.y), str(self.room.z)))

    def remove_exit(self, widget):
        """Remove an exit"""
        selection = self.exit_view.get_selection()
        model, my_iter = selection.get_selected()
        direction = model.get_value(my_iter, 0)
        del self.room.exits[direction]
        self.update(self.room)
        self.mapperwindow.handle_move()

    def add_exit(self, widget):
        """Add exit to room"""
        room_exit = self.exit_entry.get_text()
        id = self.target_entry.get_text()

        self.exit_entry.set_text("")
        self.target_entry.set_text("")

        if not self.my_map.room_by_id(id):
            return
        self.room.exits[room_exit] = self.my_map.room_by_id(id)
        self.update(self.room)

    def move_to_room(self, widget):
        """Move pointer to room"""
        selection = self.exit_view.get_selection()
        model, my_iter = selection.get_selected()
        direction = model.get_value(my_iter, 0)
        self.my_map.move(direction, None)
        self.mapperwindow.handle_move()


def on_about(event):
    """Show about dialog"""
    dialog = AboutDialog()
    dialog.win.show()

if __name__ == "__main__":

    gtk.threads_init()
    gtk.threads_enter()
    MAPPER = Mapper()
    MAPPER.show()
    MAPPER.start_mapping()
    gtk.main()
    gtk.threads_leave()


